package com.element.security.single;

import com.element.security.single.annotation.AuthExcludeRequest;
import com.element.security.single.annotation.EnableGlobalSecurity;
import com.element.security.single.constant.SecurityParams;
import com.element.security.single.custom.CustomAuthProvider;
import com.element.security.single.custom.CustomAuthenticationFilter;
import com.element.security.single.filter.TokenAuthenticationFilter;
import com.element.security.single.handler.AnonymousAuthenticationHandler;
import com.element.security.single.handler.CustomerAccessDeniedHandler;
import com.element.security.single.handler.LoginFailureHandler;
import com.element.security.single.handler.LoginSuccessHandler;
import com.element.security.single.service.IAuthUserDetailsService;
import com.element.security.single.service.IGrantTypeService;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import java.util.*;

@Component
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private LoginSuccessHandler loginSuccessHandler;

    @Resource
    private IAuthUserDetailsService authUserDetailsService;

    @Resource
    private TokenAuthenticationFilter tokenAuthenticationFilter;

    @Resource
    private IGrantTypeService grantTypeService;

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(grantTypeService);
        //登录请求url地址
        customAuthenticationFilter.setFilterProcessesUrl(SecurityParams.OAUTH_TOKEN);
        customAuthenticationFilter.setUsernameParameter(SecurityParams.USER_NAME);
        customAuthenticationFilter.setPasswordParameter(SecurityParams.PASSWORD);
        customAuthenticationFilter.setAuthenticationManager(authenticationManager());
        //认证成功
        customAuthenticationFilter.setAuthenticationSuccessHandler(loginSuccessHandler);
        //认证失败
        customAuthenticationFilter.setAuthenticationFailureHandler(new LoginFailureHandler());
        return customAuthenticationFilter;
    }

    //处理登录认证
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        List<String> excludeList = new ArrayList<>();
        excludeList.add(SecurityParams.OAUTH_TOKEN);
        Map<String, Object> beans = getApplicationContext().getBeansWithAnnotation(EnableGlobalSecurity.class);
        if (!CollectionUtils.isEmpty(beans)) {
            beans.forEach((key, value) -> {
                Class<?> clazz = value.getClass();
                EnableGlobalSecurity security = AnnotationUtils.findAnnotation(clazz, EnableGlobalSecurity.class);
                if (null != security) {
                    String[] excludes = security.exclude();
                    if (null != excludes && excludes.length > 0) {
                        excludeList.addAll(Arrays.asList(excludes));
                    }
                }
            });
        }
        RequestMappingHandlerMapping mapping = getApplicationContext().getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        map.keySet().forEach(info -> {
            HandlerMethod handlerMethod = map.get(info);
            // TODO 获取方法上边的注解 后期可加入字段判断
            AuthExcludeRequest method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), AuthExcludeRequest.class);
            Optional.ofNullable(method)
                    .ifPresent(inner -> {
                        if (null != info.getPatternsCondition()) {
                            excludeList.addAll(info.getPatternsCondition().getPatterns());
                        }
                    });
            // TODO 获取类上边的注解 后期可加入字段判断
            AuthExcludeRequest controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), AuthExcludeRequest.class);
            Optional.ofNullable(controller)
                    .ifPresent(inner -> {
                        if (null != info.getPatternsCondition()) {
                            excludeList.addAll(info.getPatternsCondition().getPatterns());
                        }
                    });
        });
        String[] excludes = new String[excludeList.size()];
        excludes = excludeList.toArray(excludes);
        //登录过程处理
        http.formLogin()    //表单登录
                .and()
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不创建Session
                .and().authorizeRequests() //设置需要拦截的请求
                .antMatchers(excludes).permitAll()//登录放行
                .anyRequest().authenticated()  //其他请求一律拦截
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new AnonymousAuthenticationHandler())  //匿名无权限类
                .accessDeniedHandler(new CustomerAccessDeniedHandler())       //认证用户无权限
                .and()
                .cors();//支持跨域
        http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterAt(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }


    //认证配置处理器
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new CustomAuthProvider(authUserDetailsService))
                .userDetailsService(authUserDetailsService)
                //密码加密
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("*");
        configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public CorsFilter corsFilter() {
        return new CorsFilter(corsConfigurationSource());
    }
}